/********************************************************************************
* File Name:	gamearea.cpp
* Description:	第14章实例训练
* Reference book:《Linux环境下Qt4图形界面与MySQL编程》，机械工业出版社.2012.1
* E_mail: openlinux2011@gmail.com
*
********************************************************************************/
#include "gamearea.h"
#include <QKeyEvent>
#include <QSound>
#include <QMessageBox>
#include "input.h"

//constructor and destructor below

GameArea::GameArea(QWidget *parent) :
    QFrame(parent)
{
    this->init_gameArea(6,6,430,430,200,400,20,60,0);
    this->init_Game();
}

GameArea::GameArea(int speed,QWidget *parent) :
    QFrame(parent)
{
    this->init_gameArea(6,6,430,430,200,400,20,60,0);
    this->init_Game();
    this->moveSpeed = speed;
    this->gameStart();
}

GameArea::~GameArea()
{

}
//core functions

//item down one step
void GameArea::moveOneStep()
{
     startY += step;
}
//display the map after an item went down
void GameArea::do_MoveNext()
{
    this->currentItem_to_currentMap();
    this->draw_gameArea();
}

void GameArea::nextItem()
{//clear rows after copying the map
    copy_Map(currentMap,copyMap,map_row,map_col);
    this->clearRow();
}

//if the game is over
bool GameArea::isGame_Over()
{
    if(this->isGameOver) return true;
    else return false;
}

//get num of the row which is full
int GameArea::getFullRowNum()
{
    return fullRowNum;
}

//set item's color or backgroud's pic
void GameArea::setGameAreaColor(QColor color)
{
    gameAreaColor = color;
}

void GameArea::setBoxBrushColor(QColor color)
{
    boxBrushColor = color;
}

void GameArea::setBoxPenColor(QColor color)
{
    boxPenColor = color;
}

void GameArea::set_draw_box_picture(bool Bool)
{
    this->is_draw_box_picture = Bool;
}

void GameArea::setBoxPicture(QString fileName)
{
    this->boxPicture = fileName;
}
//move down 1 step or move to the end
void GameArea::setKey_Down_Move_oneStep(bool Bool)
{
    this->isKey_Down_Move_OneStep = Bool;
}
//if display grid
void GameArea::setDrawGrid(bool Bool)
{
    isDrawGrid = Bool;
}

//if display the next item which will occur soon
void GameArea::setDrawNextItem(bool Bool)
{
    isDrawNextItem = Bool;
}

//add item
void GameArea::setbox(int row,int col)
{
    *(copyMap+row*map_col+col) = 1;
}

//core functions below


void GameArea::init_gameArea(int X,int Y,int frame_width,int frame_height,int width,int height,int boxStep,int start_x,int start_y)
       //init gamearea,init area where shows the next item,frame width,frame height,gameareawidth,gameareaheight,boxstep,startx,starty
{
    this->gameArea_width = width;
    this->gameArea_height = height;
    this->step = boxStep;
    this->init_startX = start_x;
    this->init_startY = start_y;
    this->map_row = gameArea_height / step;
    this->map_col = gameArea_width /step;

    //init gamearea
    this->resize(frame_width,frame_height);
    this->move(X,Y);

    pix_gameArea = new QPixmap(this->gameArea_width,this->gameArea_height);

    this->isKey_Down_Move_OneStep =true;

    //default options
    this->isDrawGrid = true;
    this->isDrawNextItem=true;
    this->is_draw_box_picture = false;

    //default color sets
    this->gameAreaColor = Qt::white;
    this->boxBrushColor = Qt::yellow;
    this->boxPenColor = Qt::green;

    this->theAngle = 0;
    this->gameArea_X = 0;
    this->gameArea_Y = 0;
    this->pix_X = 10;
    this->pix_Y = 10;

    myItem = new MyItem();

    this->currentMap = new unsigned char[map_row*map_col];
    this->copyMap = new unsigned char[map_row*map_col];   

}

void GameArea::init_Game() //init variable
{
    this->init_Map(currentMap,map_row,map_col);
    this->init_Map(copyMap,map_row,map_col);

    this->currentItem = this->currentMap;   //init pointer
    this->theNextItem = this->currentMap;   //pointer->null

    isFirstItem = true;
    isGameOver = false;
    fullRowNum = 0;

    this->draw_gameArea();
    this->score=0;
}

void GameArea::gameStart()
{
    this->startX = this->init_startX;
    this->startY = this->init_startY;
    fullRowNum = 0; //clear rows which has been fullfilled by items when the new item showed
    if(isFirstItem)
    {
        this->currentItem = myItem->getItem();
        isFirstItem = false;
    }
    else this->currentItem = this->theNextItem;
    this->theNextItem = myItem->getItem();
    this->currentItem_to_currentMap();
    this->draw_gameArea();
}

void GameArea::init_Map(unsigned char *initMap,int row,int col)
        //set array 0
{
    for(int i=0;i<row;i++)
    {
        for(int j=0;j<col;j++)
        {
            *(initMap+i*col+j)= 0;
        }
    }
}

void GameArea::draw_gameArea()  //draw gamearea
{
    this->pix_gameArea->fill(gameAreaColor);
    if(this->isDrawGrid)
    {
        draw_Grid();
    }
    draw_currentMap();
    update();
}

void GameArea::draw_Grid()   //draw grid
{
    QPainter painter(this->pix_gameArea);
    painter.setPen(Qt::DotLine);
    for(int i=0; i<map_row; i++)
    {
        painter.drawLine(QPoint(0,i*step),QPoint(this->gameArea_width,i*step));
    }
    for(int j=0; j<map_col; j++)
    {
        painter.drawLine(QPoint(j*step,0),QPoint(j*step,this->gameArea_height));
    }
}

void GameArea::draw_currentMap()         //draw current map
{
    QPainter painter(this->pix_gameArea);
    painter.setPen(this->boxPenColor);
    painter.setBrush(this->boxBrushColor);
    for(int i=0;i<map_row;i++)    //draw the item in grid to the map
    {
        for(int j=0;j<map_col;j++)
       {
             if(*(currentMap+i*map_col+j)==1)
             {
                 if(this->is_draw_box_picture)
                 {
                    QPixmap pix;
                    pix.load(this->boxPicture);
                    painter.drawPixmap(j*step,i*step,step,step,pix);
                 }

                    painter.drawRect(j*step,i*step,step,step);
             }
        }
    }
}

void GameArea::currentItem_to_currentMap()
        //draw current item to current map
{
    copy_Map(copyMap,currentMap,map_row,map_col);//use copy map
    int m_row = startY/step;
    int m_col = startX/step;
    for(int i=m_row;i<m_row+4;i++)
    {
        for(int j=m_col;j<m_col+4;j++)
        {
            *(currentMap+i*map_col+j)|=*(currentItem+(i-m_row)*4+(j-m_col));//change coordinate
        }
    }
}

void GameArea::copy_Map(unsigned char *theMap,unsigned char *toMap,int row,int col)
        //copy the map
{
    for(int i=0;i<row;i++)
    {
        for(int j=0;j<col;j++)
        {
            *(toMap+i*col+j)= *(theMap+i*col+j);
        }
    }
}

void GameArea::draw_nextItem() //display next item
{
    QPainter painter(this);
    painter.drawRect(gameArea_width+20,10,4*step,4*step);
    painter.setBrush(this->boxBrushColor);
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++)
        {
            if(*(theNextItem + i*4 +j)==1)
            {
                if(this->is_draw_box_picture)
                 {
                    QPixmap pix;
                    pix.load(this->boxPicture);
                    painter.drawPixmap(gameArea_width+20+j*step,10+i*step,step,step,pix);
                 }
                painter.drawRect(gameArea_width+20+j*step,10+i*step,step,step);
            }
        }
}

void GameArea::paintEvent(QPaintEvent *e)  //repaint
{
    QPainter painter(this);
    painter.drawPixmap(QPoint(pix_X,pix_Y),*pix_gameArea);
    if(this->isDrawNextItem)  //if display next item
    {
        draw_nextItem();
    }
}

void GameArea::keyPressEvent(QKeyEvent *event)  //keyboard event
{
    switch(event->key())
    {
        case Qt::Key_Left :   //press left
        {
            startX=startX - step;
            if(isMoveLeft()) startX = startX+step;
            else
            {
                currentItem_to_currentMap();
                this->draw_gameArea();
            }
            break;
        }
        case Qt::Key_Right :    //press right
        {
            startX=startX + step;
            if(isMoveRight()) startX = startX-step;
            else
            {
                currentItem_to_currentMap();
                this->draw_gameArea();
            }

            break;
        }
        case Qt::Key_Up :     //press up
        {
            do_itemChange();
            currentItem_to_currentMap();
            this->draw_gameArea();
            break;
        }
        case Qt::Key_Down :    //press down
        {
            if(this->isKey_Down_Move_OneStep)   //default move one step one time
            {  
                startY+=step;
                if(this->isMoveEnd()) startY-=step; //move down
                else
                {
                    currentItem_to_currentMap();
                    this->draw_gameArea();
                    /*if(isPlaySound_moveDown)
                    {
                        playSound(sound_moveDown);
                    }*/
                }
            }
            else            //move to the bottom
            {
                int i=0;
                while(!this->isMoveEnd())
                {
                    startY +=step;
                    i++;
                }
                startY -=step;
                if(i>0)
                {
                    currentItem_to_currentMap();
                    this->draw_gameArea();
                }
            }

            break;
        }
        default: QFrame::keyPressEvent(event);    //other key
    }
}
bool GameArea::isMoveEnd()
        //if the item move to the bottom or overlap with others
{
    int item_endRow = myItem->currentItem_endPos(currentItem,'d');

    int itemRow = startY/step;  //item current position
    int itemCol = startX/step;


    if(itemRow+item_endRow>=map_row)//if item at bottom
    {
        return true;
    }
    for(int i=item_endRow;i>=0;i--) //if item overlap with others
    {
        for(int j=3;j>=0;j--)
        {
           if(*(copyMap+(itemRow+i)*map_col+itemCol+j) && *(currentItem+i*4+j)==1)
            {

               if(startX==init_startX&&startY<=20) isGameOver = true;
               //if at beginning overlap with others, that means game over

               return true;
            }
        }
    }
    return false;
}

bool GameArea::isMoveLeft()
        //if item at very left or overlap with others
{
    int item_endLeft = myItem->currentItem_endPos(currentItem,'l');
    int item_Col = startX/step;
    if(item_Col+item_endLeft<0)
        return true;

     if(isMoveEnd())
    {
        return true;
    }
    return false;
}

bool GameArea::isMoveRight()
        //if item at very right or overlap with others
{
    int item_endRight = myItem->currentItem_endPos(currentItem,'r');
    int item_Col = startX/step;
    if(item_Col+item_endRight >= map_col)
        return true;
    if(isMoveEnd())
    {
        return true;
    }
    return false;
}
void GameArea::do_itemChange()//how to deal with those items over the area or overlap with others
{
    unsigned char *tempItem;
    tempItem = currentItem; //copy current item
    currentItem = myItem->itemChange(currentItem); //change the item position
    if(startX<0) startX = 0; //over left, let it at very left
    if((startX + 4*step)> gameArea_width) startX = gameArea_width - 4*step;
    //over right,let it at very right
    if(isMoveEnd())
    //if overlap with others, do not let it change
    {
        currentItem = tempItem;
    }
}
void GameArea::clearRow() //clear full rows
{
    int temp1 = 1;
    int clearOneTime = 0;  // use to calculate score

    for(int i=map_row-1;i>0;i--) //from down to up
    {
        for(int j=0;j<map_col;j++)
        {
            temp1 = *(copyMap+i*map_col+j) && temp1;
        }
        if(temp1) //if after that,temp1 still equals 1, that means it is a full row
        {
            fullRowNum++;
            for(int row = i;row>0;row--)   //let current row equals the up one
                for(int col = 0;col<map_col;col++)
                 {
                     *(copyMap+row*map_col+col)=*(copyMap+(row-1)*map_col+col);
                 }
            i=i+1;
            clearOneTime ++;
        }
        temp1 =1;
    }

    if(clearOneTime == 0)      //calculate the score
    {//do nothing
    }
    else if(clearOneTime == 1)
    {
        score += 1;
    }
    else if(clearOneTime == 2)
    {
        score += 5;
    }
    else if(clearOneTime == 3)
    {
        score += 30;
    }
    else if(clearOneTime == 4)
    {
        score += 100;
    }
}

